﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using SMSROUTER.Business;
using System.Threading.Tasks;
using Settings;
using StorageManagement;
using System.Configuration;
using System.Threading;
using System.Web.Services.Protocols;

namespace PluggableModulesInterface
{
    public abstract class WorkingBaseMTRouter : WorkingBaseRouter
    {
        #region Properties and Fields
        public bool ENABLE_GETREPORT
        {
            get
            {
                try
                {
                    return bool.Parse(GetConfig("EnableGetReport"));
                }
                catch
                {
                    return false;
                }
            }
            set
            {
                SetConfig("EnableGetReport", value.ToString());
            }
        }
        #endregion Properties and Fields

        #region SyncLoop
        public override void Start()
        {
            Stop();
            if (this.Router != null)
            {
                _IsStart = true;
                if (taskList.Count < NumOfThread)
                {
                    for (int i = 0; i < NumOfThread; i++)
                    {
                        taskList.Add(Task.Factory.StartNew(ActionSent, TaskCreationOptions.LongRunning));
                    }
                }
                if (ENABLE_GETREPORT)
                {
                    if (taskList.Count < NumOfThread + 1)
                    {
                        taskList.Add(Task.Factory.StartNew(ActionGet, TaskCreationOptions.LongRunning));
                    }
                }
            }
        }

        protected void ActionSent()
        {
            try
            {
                while (_IsStart)
                {
                    try
                    {
                        string routerUrl = "";
                        if (CheckRouter(ref routerUrl))
                        {
                            SendSms(routerUrl);
                        }
                    }
                    catch (AggregateException ex)
                    {
                        foreach (var item in ex.InnerExceptions)
                            HandleException(ex, RouterName);
                    }
                    catch (Exception ex)
                    {
                        HandleException(ex, RouterName);
                    }
                    finally
                    {
                        if (_IsStart)
                        {
                            Thread.Sleep(INFINITE_NEXT_TIME);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, "WorkingBaseMORouter");
            }
        }

        private void ActionGet()
        {
            try
            {
                while (_IsStart)
                {
                    try
                    {
                        string routerUrl = "";
                        if (CheckRouter(ref routerUrl))
                        {
                            GetReport(routerUrl);
                        }
                    }
                    catch (AggregateException ex)
                    {
                        foreach (var item in ex.InnerExceptions)
                            HandleException(ex, RouterName);
                    }
                    catch (Exception ex)
                    {
                        HandleException(ex, RouterName);
                    }
                    finally
                    {
                        if (_IsStart)
                        {
                            Thread.Sleep(INFINITE_NEXT_TIME);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, "WorkingBaseRouter");
            }
        }

        protected virtual void SendSms(string routerUrl)
        {
            var smsOutList = SmsOutWaitingStorage.DequeueElementData(Key, AppSetting.BatchSize);
            if (smsOutList != null && smsOutList.Count > 0)
            {
                //Danh sách sms bị từ chối do số điện thoại nằm trong blacklist
                var clientRefuseList = smsOutList.Where(item => ClientRefuse.GetInstance().Contains(item.DestAddr)).ToList();

                //Danh sách sms bị từ chối do ShortCode không hợp lệ
                var shortCodeRefuseList = smsOutList.Where(item => SmsShortCode.FindItem(obj => obj.CooperateID == item.CooperateID && obj.RouterID == item.RouterID &&
                                            obj.ShortCode == item.ShortCode && obj.OperatorID == item.OperatorID
                                            && obj.MTTypeID == item.MTTypeID && obj.Enabled == true) == null).ToList();
                
                //Danh sách sms hợp lệ được gửi qua router
                var smsList = smsOutList.Except(clientRefuseList).Except(shortCodeRefuseList).ToList();
                ClientRefuseSmsList(clientRefuseList);
                ShortCodeRefuseSmsList(shortCodeRefuseList);
                if (smsList.Count > 0)
                {
                    SendSms(smsList);
                }
            }
        }

        protected abstract void SendSms(List<SmsOutWaiting> smsOutWaitingList);

        protected virtual void GetReport(string routerUrl)
        {
            GetReport();
        }

        protected virtual void GetReport() { }
        #endregion

        #region method
        protected virtual void ClientRefuseSmsList(IList<SmsOutWaiting> refuseSource)
        {
            if (refuseSource != null && refuseSource.Count > 0)
            {
                try
                {
                    SmsOutReportStorage.EnqueueElementData(Key, SmsOutReportStruct.CreateList(refuseSource, DateTime.Now, DateTime.Now, AppSetting.Status.FAIL, ThreadID));
                    string log_msg = string.Format("Client refuse list: [{0}]", string.Join(";", refuseSource.Select(p => p.DestAddr + " : " + p.SmsOutGUID)));
                    Log.InsertLog(log_msg, "SendSMS", ObjectType, RouterName, null);
                }
                catch (Exception ex)
                {
                    HandleException(ex, RouterName, refuseSource);
                }
            }
        }

        protected virtual void ShortCodeRefuseSmsList(IList<SmsOutWaiting> refuseSource)
        {
            if (refuseSource != null && refuseSource.Count > 0)
            {
                try
                {
                    SmsOutReportStorage.EnqueueElementData(Key, SmsOutReportStruct.CreateList(refuseSource, DateTime.Now, DateTime.Now, AppSetting.Status.FAIL, ThreadID));
                    string log_msg = string.Format("ShortCode refuse list: [{0}]", string.Join(";", refuseSource.Select(p => p.SmsOutGUID + " : " + p.ShortCode)));
                    Log.InsertLog(log_msg, "SendSMS", ObjectType, RouterName, null);
                }
                catch (Exception ex)
                {
                    HandleException(ex, RouterName, refuseSource);
                }
            }
        }
        #endregion method

        #region Handle Exception
        /// <summary>
        /// Xử lý tin nhắn khi fail
        /// </summary>
        protected override void RetryOnFailure(dynamic obj, string errorMessage)
        {
            var sms = obj as SmsOutWaiting;
            if (sms != null)
            {
                if (sms.RetryOnFailReqTimes.HasValue && sms.RetryOnFailReqTimes.Value > 0)
                {
                    sms.RetryOnFailReqTimes = sms.RetryOnFailReqTimes.Value - 1;
                    SmsOutWaitingStorage.EnqueuData(sms);
                    Log.InsertLog(string.Format("Retry On Failure [SmsInGUID = {0}]. Retry remaining: {1}. Error: {2}", sms.SmsOutGUID, sms.RetryOnFailReqTimes, errorMessage), "RetryOnFailure", ObjectType, RouterName, sms.CooperateID);
                }
                else
                {
                    SmsOutReportStorage.SetStatus(Key, sms, DateTime.Now, AppSetting.Status.FAIL, ThreadID);
                    Log.InsertLog(string.Format("Retry On Failure [SmsOutGUID = {0}], Set Status = '{1}'. Error: {2}", sms.SmsOutGUID, AppSetting.Status.FAIL, errorMessage), "RetryOnFailure - FAIL", ObjectType, RouterName, sms.CooperateID);
                }
            }
        }

        /// <summary>
        /// Xử lý tin nhắn khi timed out
        /// </summary>
        protected override void RetryOnTimedOut(dynamic obj, string errorMessage)
        {
            var sms = obj as SmsOutWaiting;
            if (sms != null)
            {
                if (sms.RetryOnTimeOutReqTimes.HasValue && sms.RetryOnTimeOutReqTimes.Value > 0)
                {
                    sms.RetryOnTimeOutReqTimes = sms.RetryOnTimeOutReqTimes.Value - 1;
                    SmsOutWaitingStorage.EnqueuData(sms);
                    Log.InsertLog(string.Format("Retry On Timeout [SmsInGUID = {0}]. Retry remaining: {1}. Error: {2}", sms.SmsOutGUID, sms.RetryOnFailReqTimes, errorMessage), "RetryOnTimedOut", ObjectType, RouterName, sms.CooperateID);
                }
                else
                {
                    SmsOutReportStorage.SetStatus(Key, sms, DateTime.Now, AppSetting.Status.TIMEOUT, ThreadID);
                    Log.InsertLog(string.Format("Retry On Timeout [SmsOutGUID = {0}], Set Status = '{1}'. Error: {2}", sms.SmsOutGUID, AppSetting.Status.FAIL, errorMessage), "RetryOnTimedOut - FAIL", ObjectType, RouterName, sms.CooperateID);
                }
            }
        }
        #endregion Handle Exception
    }
}
